#!/usr/bin/env bash

set -ue
set -o pipefail

USER_PWD=$PWD
EXEC_NAME=$0

cd $CJ_ROOT
LOGDIR=$PWD/logs

if [ -e $LOGDIR ]; then
    rm -Rf $LOGDIR
fi
mkdir -p $LOGDIR

function log() {
    FILENAME=$1
    tee $LOGDIR/$FILENAME.log | xargs -d$'\n' -I '{}' printf "\33[2K\r[%s] %s" "$FILENAME" "{}"
}

function run_with_log() {
    LOG_NAME=$1
    shift

    ($@ |& log $LOG_NAME) \
        && echo -e "\n\e[0;32mOK\e[0m \e[1m$LOG_NAME\e[0m" \
        || (echo -e "\n\e[0;31mFAILED\e[0m \e[1m$LOG_NAME\e[0m"; return 1)
}

USAGE_CJC=$(cat <<EOF
Usage: $EXEC_NAME ... cjc [options]
Build compiler

Options:
    -h, --help      Show this message
    -f, --fast      Compile just cjc (ninja cjc-release)
    
    --clean         Do "build.py clean" before build
    --utest         Compile unit tests (clean build required)
EOF
)

function build_cjc() {

    DO_CLEAN="No"
    NO_TEST="--no-test"
    
    CJC_ONLY="No"

    while [[ $# -gt 0 ]]; do
        case $1 in
            --clean)
                shift
                DO_CLEAN="Yes"
                ;;
            --utests)
                shift
                NO_TEST=""
                ;;
            -f|--fast)
                shift
                CJC_ONLY="Yes"
                ;;
            -h|--help)
                echo "$USAGE_CJC"
                exit 0
                ;;
            -*|--*)
                echo "Unknown option $1"
                exit 1
                ;;
        esac
    done

    pushd cangjie_compiler > /dev/null

    echo "[-] Building cjc..."

    if [[ "$CJC_ONLY" == "Yes" ]]; then
        pushd build/build > /dev/null

        run_with_log "ninja_cjc" ninja cjc-release

        popd > /dev/null
    else
        if [[ "$DO_CLEAN" == "Yes" ]]; then
            ./build.py clean
        fi

        run_with_log "cjc" ./build.py build -t debug -j$JOBS $NO_TEST
    fi

    run_with_log "cjc_install" ./build.py install

    # FIXME(iliayar): Is it needed
    if [[ -z "${CANGJIE_HOME:-}" ]]; then
        direnv reload
    fi

    if [[ -e ./output/bin/.cjc-wrapped ]]; then
        rm -Rf ./output/bin/.cjc-wrapped*
    fi
    $NIX_WRAP_CJC $CANGJIE_HOME/bin/cjc
    rm $CANGJIE_HOME/bin/cjc-frontend
    ln -s $CANGJIE_HOME/bin/.cjc-wrapped $CANGJIE_HOME/bin/cjc-frontend

    popd > /dev/null
}

function build_runtime_impl() {
    pushd cangjie_runtime/runtime > /dev/null

    echo "[-] Building runtime..."

    if [[ "$DO_CLEAN" == "Yes" ]]; then
        python3 build.py clean
    fi

    run_with_log "runtime"                python3 build.py build -t debug --version 1.0.0
    run_with_log "runtime_install"        python3 build.py install
    run_with_log "runtime_install_prefix" python3 build.py install --prefix $PWD/../../cangjie_compiler/output

    popd > /dev/null
}


USAGE_RUNTIME=$(cat <<EOF
Usage: $EXEC_NAME ... runtime [options]
Build runtime

Options:
    -h, --help      Show this message
    
    --clean         Do "build.py clean" before build
EOF
)

function build_runtime() {
    DO_CLEAN="No"

    while [[ $# -gt 0 ]]; do
        case $1 in
            --clean)
                shift
                DO_CLEAN="Yes"
                ;;
            -h|--help)
                echo "$USAGE_RUNTIME"
                exit 0
                ;;
            -*|--*)
                echo "Unknown option $1"
                exit 1
                ;;
        esac
    done

    build_runtime_impl
}

USAGE_STD=$(cat <<EOF
Usage: $EXEC_NAME ... std [options]
Build std

Options:
    -h, --help      Show this message
    
    --clean         Do "build.py clean" before build
    --mock          Compile with mock enabled (--mock=on) (clean build required)
EOF
)

function build_std() {
    DO_CLEAN="No"
    CJC_FLAGS=()

    while [[ $# -gt 0 ]]; do
        case $1 in
            --clean)
                shift
                DO_CLEAN="Yes"
                ;;
            --mock)
                shift
                CJC_FLAGS+=("--mock" "on")
                ;;
            -h|--help)
                echo "$USAGE_STD"
                exit 0
                ;;
            -*|--*)
                echo "Unknown option $1"
                exit 1
                ;;
        esac
    done

    RUNTIME_LIB="$PWD/cangjie_runtime/runtime/output"
    if [ ! -e $RUNTIME_LIB ]; then
        build_runtime_impl
    fi

    pushd cangjie_runtime/stdlib > /dev/null

    echo "[-] Building std..."

    if [[ "$DO_CLEAN" == "Yes" ]]; then
        ./build.py clean
    fi

    BUILD_ARGS=""
    for arg in ${CJC_FLAGS[@]}; do
        BUILD_ARGS="$BUILD_ARGS --build-args=$arg"
    done


    run_with_log "std"         ./build.py build -t debug -j$JOBS --target-lib $RUNTIME_LIB $BUILD_ARGS
    run_with_log "std_install" ./build.py install --prefix $PWD/../../cangjie_compiler/output

    popd > /dev/null
}

USAGE_STDX=$(cat <<EOF
Usage: $EXEC_NAME ... stdx [options]
Build stdx

Options:
    -h, --help      Show this message
    
    --clean         Do "build.py clean" before build
EOF
)

function build_stdx() {
    DO_CLEAN="No"

    while [[ $# -gt 0 ]]; do
        case $1 in
            --clean)
                shift
                DO_CLEAN="Yes"
                ;;
            -h|--help)
                echo "$USAGE_STDX"
                exit 0
                ;;
            -*|--*)
                echo "Unknown option $1"
                exit 1
                ;;
        esac
    done

    pushd cangjie_stdx > /dev/null

    echo "[-] Building stdx..."

    if [[ "$DO_CLEAN" == "Yes" ]]; then
        ./build.py clean
    fi

    run_with_log "stdx"        ./build.py build -t debug -j$JOBS
    run_with_log "stdx_install" ./build.py install

    # FIXME(iliayar): Is it needed
    if [[ -z "${CANGJIE_STDX_PATH:-}" ]]; then
        direnv reload
    fi

    popd > /dev/null
}

USAGE_TEST=$(cat <<EOF
Usage: $EXEC_NAME ... test [options] FILES...
Run LLT/HLT tests

Arguments:
    FILES           Test files/diretories

Options:
    -h, --help      Show this message
    
    --test_cfg      Custom test config (default: configs/cjnative/cjnative_test.cfg)
    --test_list     Custom test list (default: cjnative_testlist)
EOF
)


function run_test() {
    TEST_CFG=$CJ_ROOT/cangjie_test/testsuites/LLT/configs/cjnative/cjnative_test.cfg
    TEST_LIST=$CJ_ROOT/cangjie_test/testsuites/LLT/cjnative_testlist

    TESTS=()

    while [[ $# -gt 0 ]]; do
        case $1 in
            --test_cfg)
                shift
                TEST_CFG=$1
                shift
                ;;
            --test_list)
                shift
                TEST_LIST=$1
                shift
                ;;
            -h|--help)
                echo "$USAGE_TEST"
                exit 0
                ;;
            -*|--*)
                echo "Unknown option $1"
                exit 1
                ;;
            *)
                TESTS+=($1)
                shift
                ;;
        esac
    done

    pushd $USER_PWD > /dev/null

    python3 $CJ_ROOT/cangjie_test_framework/main.py \
        --test_cfg $TEST_CFG \
        --test_list $TEST_LIST \
        --fail-verbose -pFAIL -j$JOBS --debug \
        ${TESTS[@]}

    popd > /dev/null
    exit 0
}

USAGE_TEST_LOG=$(cat <<EOF
Usage: $EXEC_NAME ... testlog FILE
Show failed test log

Arguments:
    FILE            File with test log test_name.log

Options:
    -h, --help      Show this message
EOF
)

function show_test_log() {
    LOG_FILE=$1

    $EDITOR $CJ_ROOT/cangjie_test_framework/test_temp/log/$LOG_FILE
}

USAGE=$(cat <<EOF
Usage: $EXEC_NAME [options] [command] ...
Cangjie tool with usefull scripts

Options:
    -v, --verbose   Show what is executed (set -x)
    -h, --help      Show this message

Commands:
    cjc          build compiler
    std          build std
    stdx         build stdx
    runtime      build runtime
    test         run tests
    testlog      show test log
EOF
)

VERBOSE="No"

while [[ ${1:-} =~ -.* ]]; do
    case ${1:-} in
        -v|--verbose)
            shift
            VERBOSE="Yes"
            ;;
        -h|--help)
            shift
            echo "$USAGE"
            exit 0
            ;;
        -*|--*)
            echo "Unknown option $1"
            exit 1
            ;;
    esac
done

if [[ $VERBOSE = "Yes" ]]; then
    set -x
fi

case ${1:-} in
    cjc)
        shift
        build_cjc $@
        ;;
    std)
        shift
        build_std $@
        ;;
    stdx)
        shift
        build_stdx $@
        ;;
    runtime)
        shift
        build_runtime $@
        ;;
    test)
        shift
        run_test $@
        ;;
    testlog)
        shift
        show_test_log $@
        ;;
    *)
        if [[ -z ${1:-} ]]; then
            echo "No command specified"
        else
            echo "Unknown command ${1:-}"
        fi
        echo "$USAGE"
        exit 1
        ;;
esac
